package org.yuxuan.command.api;

import java.util.Arrays;
import java.util.Collections;
import java.util.Locale;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.bukkit.command.CommandMap;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.Plugin;

import org.coherent.core.Command;
import static org.coherent.core.Command.*;
import org.coherent.core.Command.Binding;
import static org.coherent.core.Command.Binding.*;
import org.coherent.core.Command.Dispatcher;
import static org.coherent.core.Command.Dispatcher.*;
import org.coherent.core.Command.Behavior;
import static org.coherent.core.Command.Behavior.*;

import org.jparsec.core.Parser.Result;
import static org.jparsec.core.Parser.Result.*;
import org.jparsec.core.Parser.Location;
import static org.jparsec.core.Parser.Location.*;
import org.jparsec.core.Text;
import static org.jparsec.core.Text.*;

import org.monadium.core.data.Bottom;
import static org.monadium.core.data.Bottom.*;
import org.monadium.core.data.List;
import static org.monadium.core.data.List.*;
import org.monadium.core.data.Unit;
import static org.monadium.core.data.Unit.*;

public final class CoherentCommand<A> extends org.bukkit.command.Command {
	private final Plugin plugin;
	private final Command<CommandSender, Unit, A> command;
	private final Command<CommandSender, Unit, A> root;

	public CoherentCommand(Plugin plugin, Command<CommandSender, Unit, A> command, String... aliases) {
		super(command.name(), command.description(), "/" + command.name() + " <args>", Arrays.asList(aliases));

		this.plugin = plugin;
		this.command = command;
		this.root = command(
			"root",
			"Root command",
			Body::empty,
			dispatcher(Stream.concat(Stream.of(command.name()), Arrays.stream(aliases))
				.flatMap(name -> Stream.of(name, plugin.getName().toLowerCase(Locale.ENGLISH).trim() + ":" + name))
				.map(name -> Binding.<CommandSender, Unit, Unit, A> entry(name, command))
				.reduce(nil(), (bindings, binding) -> cons(binding, bindings), List::concat)
			),
			stub()
		);
	}

	public Plugin getPlugin() {
		return plugin;
	}

	public Command<CommandSender, Unit, A> getCommand() {
		return command;
	}

	public boolean registerTo(CommandMap commandMap) {
		return commandMap.register(plugin.getName().toLowerCase(Locale.ENGLISH).trim(), this, plugin);
	}

	public boolean unregisterFrom(CommandMap commandMap) {
		return commandMap.unregister(this);
	}

	@Override
	public boolean internalExecute(CommandSender sender, String commandLabel, String commandLine) {
		Result<Text, Context<CommandSender, Unit>, Bottom, Action<A>> result = runCommand(root, text(commandLine), sender, unit());
		if (result.isSuccess()) {
			Action<A> action = result.coerceResult();
			if (action.isHandled()) {
				action.coerceHandled().get();
			} else if (action.isRejected()) {
				action.coerceRejected().run();
			}
		} else if (result.isFail()) {
			sender.sendMessage(result.getEnvironment().logger().toString());
		}
		return result.isSuccess();
	}

	@Override
	public java.util.List<String> internalTabComplete(CommandSender sender, String commandLabel, String commandLine, org.bukkit.Location location) throws IllegalArgumentException {
		Result<Text, Context<CommandSender, Unit>, Bottom, List<Completion>> result = completeCommand(root, text(commandLine), sender, unit());
		if (result.isSuccess()) {
			List<Completion> completions = result.coerceResult();
			return completions.stream().map(completion -> {
				int offset = completion.location().offset();
				String prefix = commandLine.substring(0, Math.min(Math.max(offset, 0), commandLine.length()));
				String suggestion = completion.completion().toString();
				return prefix + suggestion;
			}).collect(Collectors.toList());
		} else {
			return Collections.emptyList();
		}
	}

	@Override
	public boolean execute(CommandSender sender, String commandLabel, String[] args) {
		return false;
	}
}
